#import "UIControl_SH.h"
#import <objc/runtime.h>

const void *kKeyHitEdgeInsets = "SH88385b693d8ef0e6d58f769d9ca9be23";
const void *kSpeedinessDisabledKey       = "kSpeedinessDisabledKey";
const void *kSh_Ignore_Click_Aciton_Flag = "kSH_Ignore_Click_Aciton_Flag";
#define kDuration 1.f

@interface UIControl (SH_Flag)
@property (nonatomic, assign) BOOL sh_ignore_click_aciton_flag;
@end

@implementation UIControl (SH_Flag)

- (BOOL)aw_ignore_click_aciton_flag
{
    id obj = objc_getAssociatedObject(self, kSh_Ignore_Click_Aciton_Flag);
    if ([obj isKindOfClass:NSNumber.class]) {
        return [(NSNumber *)obj boolValue];
    }
    return false;
}

- (void)setAw_ignore_click_aciton_flag:(BOOL)flag
{
    objc_setAssociatedObject(self, kSh_Ignore_Click_Aciton_Flag, @(flag), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}


@end


@implementation UIControl (SH)

@dynamic hitEdgeInsets;

- (void)setHitEdgeInsets:(UIEdgeInsets)hitEdgeInsets
{
    NSValue *value = [NSValue value:&hitEdgeInsets withObjCType:@encode(UIEdgeInsets)];
    objc_setAssociatedObject(self, kKeyHitEdgeInsets, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (UIEdgeInsets)hitEdgeInsets
{
    NSValue *value = objc_getAssociatedObject(self, kKeyHitEdgeInsets);
    if (value) {
        UIEdgeInsets edgeInsets;
        [value getValue:&edgeInsets];
        return edgeInsets;
    } else {
        return UIEdgeInsetsZero;
    }
}

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    if (UIEdgeInsetsEqualToEdgeInsets(self.hitEdgeInsets, UIEdgeInsetsZero) || !self.enabled || self.hidden) {
        return [super pointInside:point withEvent:event];
    }

    CGRect hitFrame = UIEdgeInsetsInsetRect(self.bounds, self.hitEdgeInsets);
    return CGRectContainsPoint(hitFrame, point);
}

+ (void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        SEL    oldSel    = @selector(sendAction:to:forEvent:);
        SEL    newSel    = @selector(sh_sendAction:to:forEvent:);
        Method oldMethod = class_getInstanceMethod(self, oldSel);
        Method newMethod = class_getInstanceMethod(self, newSel);
        if (class_addMethod(self, oldSel, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) {
            class_replaceMethod(self, newSel, method_getImplementation(oldMethod), method_getTypeEncoding(oldMethod));
        } else {
            method_exchangeImplementations(oldMethod, newMethod);
        }
    });
}

- (void)sh_sendAction:(SEL)action to:(nullable id)target forEvent:(nullable UIEvent *)event
{
    if (self.speedinessDisabled) {
        if (self.sh_ignore_click_aciton_flag) {
            return;
        }
        self.sh_ignore_click_aciton_flag = true;
        __weak __typeof(self) weakSelf   = self;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kDuration * NSEC_PER_SEC)), dispatch_get_main_queue(),
                       ^{
                           __strong __typeof(weakSelf) strongSelf = weakSelf;
                           strongSelf.sh_ignore_click_aciton_flag = false;
                       });
    }
    
    [self sh_sendAction:action to:target forEvent:event];
}

- (BOOL)speedinessDisabled
{
    id obj = objc_getAssociatedObject(self, kSpeedinessDisabledKey);
    if ([obj isKindOfClass:NSNumber.class]) {
        return [(NSNumber *)obj boolValue];
    }
    return false;
}

- (void)setSpeedinessDisabled:(BOOL)speedinessDisabled
{
    objc_setAssociatedObject(self, kSpeedinessDisabledKey, @(speedinessDisabled), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end

